/*
 * Copyright 2012 Alain Pannetrat (L1L1@gmx.com)
 * 
 * This file is part of cookie-miner.
 * 
 * cookie-miner is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version.
 * 
 * cookie-miner is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License 
 * along with cookie-miner. If not, see http://www.gnu.org/licenses/.
 *
 * @version: $Id$
 *
 */
#include <ncurses.h>
#include <map>
#include <vector>
#include <string>
#include <strings.h>
#include <stdio.h>
#include "log_facility.h"
#include "http_packet.h"

FILE *stdlog = NULL;

WINDOW *TOP_WINDOW = NULL;
WINDOW *BOT_WINDOW = NULL;
int SCREEN_WIDTH = 80;
int SCREEN_HEIGHT = 25;

void draw_text(WINDOW *win, int len, const std::string& s);


class Widget {
	public: 
		virtual void refresh() { wrefresh(_win); }
		virtual void draw() { werase(_win); }
		virtual bool key_event(int ch) { return false; }
		virtual ~Widget() { wclear(_win); }

		bool schedule_redraw() const 			{ return _dirty; }
		void schedule_redraw(bool redraw_content) 	{ _dirty = redraw_content; }

		int width() const { return _width; }
		int height() const { return _height; }
	protected:
		Widget(WINDOW *win) : _win(win), _dirty(true) { getmaxyx(_win, _height, _width); fprintf(stdlog,"create win %p(%i,%i)\n",(void*)_win,_width,_height); }
		
		WINDOW* _win;
		int _width;
		int _height;
		int _dirty;

	private:
		Widget(const Widget&);
		const Widget& operator=(const Widget&);
};

class ListWidgetItem {
	public:
		ListWidgetItem(const std::string &new_label="...", unsigned new_id=0) : 
			_id(new_id), _label(new_label) 		{ }
		void id(int new_id) 				{ _id = new_id; }
		int id() const	 				{ return _id; }
		void label(const std::string &new_label) 	{ _label = new_label; }
		const std::string &label() const		{ return _label; }
		void draw(WINDOW *win, unsigned limit) 		{ draw_text(win,limit,_label); }
	private:
		int _id;
		std::string _label;
		
};

typedef std::map<int,int> map_ii;

class ListWidget : public Widget {
	public:
		static ListWidget* create(WINDOW *win) { return new ListWidget(win); }
		virtual bool key_event(int ch);
		virtual void draw();
		virtual int set_item(int pos, const ListWidgetItem& item);
		virtual int get_item(int pos, ListWidgetItem &item);
		virtual int locate_id(int id);
	protected:	
		ListWidget(WINDOW *win): Widget(win), _origin(0) { } 
		std::vector<ListWidgetItem> _lines;
		map_ii _lines_by_id;
		int _origin;

	private:
		ListWidget(const Widget&);
		const ListWidget& operator=(const Widget&);
};

/***/
void draw_text(WINDOW *win, int len, const std::string& s)
{
	int real_len = 0;
	unsigned i = 0;

	if (len<0) len=s.size();

	while (real_len < len && i<s.size())
	{
		if (i+3<s.size() && s[i]=='[' && s[i+1]=='/' && s[i+3]==']')
		{
			if (s[i+2]=='/')
			{
				waddch(win,'[');
				waddch(win,'/');
				real_len += 2;
			}
			else
			{
				switch (s[i+2]) {
					case 'n': wattroff(win,A_NORMAL); break;
					case 's': wattroff(win,A_STANDOUT); break;
					case 'u': wattroff(win,A_UNDERLINE); break;
					case 'r': wattroff(win,A_REVERSE); break; 
					case 'd': wattroff(win,A_DIM); break;
					case 'b': wattroff(win,A_BOLD); break;
					case 'N': wattron(win,A_NORMAL); break;
					case 'S': wattron(win,A_STANDOUT); break;
					case 'U': wattron(win,A_UNDERLINE); break;
					case 'R': wattron(win,A_REVERSE); break; 
					case 'D': wattron(win,A_DIM); break;
					case 'B': wattron(win,A_BOLD); break;
					case '1':  
					case '2':  
					case '3':  
					case '4':  
					case '5':  
					case '6':  
					case '7':
					case '8': wattron(win,COLOR_PAIR(s[i+2]-'0')); break;
				}
			}
			i+=4;
		}
		else
		{
			if (s[i]>=' ')
				waddch(win,s[i]);
			else
				waddch(win,'?');
			i++;
			real_len++;
		}
	}
}

void ListWidget::draw()
{
	Widget::draw();

	wmove(_win,0,0);
	draw_text(_win,-1,"--*:[/B]Packets[/b]:*--");
	
	for (int i=0; i<_height-1; i++)
	{
		if (_origin+i<(int)_lines.size())
		{
			wmove(_win,i+1,0);
			wprintw(_win,"%6i ",_lines[_origin+i].id());
			_lines[_origin+i].draw(_win,_width-7);
		}
	}
}

bool ListWidget::key_event(int ch)
{
	switch (ch) {
		case 'u':
			if (_origin>0) _origin--;
			schedule_redraw(true);
			return true;
		case 'd':
			if (_lines.size()>0)
			{ 
				if (_origin<(int)_lines.size()-_height+1) _origin++;
				schedule_redraw(true);
			}	
			return true;
	}
	return false;
}

int ListWidget::locate_id(int id)
{
	map_ii::iterator it = _lines_by_id.find(id);

	if (it!=_lines_by_id.end())
		return it->second;
	return -1;
}

int ListWidget::set_item(int pos, const ListWidgetItem& item)
{
	if (pos<0)
	{
		_lines_by_id.insert(std::pair<unsigned,unsigned>(item.id(),_lines.size()));		
		_lines.push_back(item);
		return _lines.size()-1;
	}
	if (pos>=(int)_lines.size())
		return -1;
	_lines[pos]=item;
	return pos;
}

int ListWidget::get_item(int pos, ListWidgetItem &item)
{
	if (pos<0 || pos>=(int)_lines.size())
		return -1;

	item = _lines[pos];
	return pos;	
}

bool load_packet(HttpPacket *packet, void *extra)
{
	ListWidget *lst = (ListWidget*) extra;
	std::string hln = packet->headline();
	ListWidgetItem item;
	int pos;
	
	if (hln.find("HTTP")==0)
	{
		pos = lst->locate_id(packet->parent_id());
		if (pos>=0)
			lst->set_item(pos+1,ListWidgetItem(" -> "+hln,packet->id()));
	}
	else
	{
		lst->set_item(-1, ListWidgetItem(hln,packet->id()));
		lst->set_item(-1, ListWidgetItem(" -> waiting...",0));
	}

	return true;
}

int main(int argc, char **argv)
{
	HttpPacketDB db;
	ListWidget *lst;
	int ch;
	int running = 1;

	logger.set_verbosity(logger.LOG_NONE);

	stdlog = fopen("std.log","w");

	initscr();
	cbreak();
	keypad(stdscr,TRUE);
	noecho();

	getmaxyx(stdscr,SCREEN_HEIGHT,SCREEN_WIDTH);
	TOP_WINDOW = newwin(SCREEN_HEIGHT-3,SCREEN_WIDTH,0,0);
	printw("Loading...");

	lst = ListWidget::create(TOP_WINDOW);
	
	refresh();
	db.open(true);
	db.iterate("packet_id<100",load_packet,lst);
	db.close();
	
	lst -> draw();
	lst -> refresh();
	lst -> schedule_redraw(false);
	//redrawwin(TOP_WINDOW);

	while (running)
	{
		ch = wgetch(stdscr);
		
		if (ch=='x') 
		{
			running =  0;
		}
		else 
			lst->key_event(ch);

		if (lst->schedule_redraw())
		{
			lst->draw();
			lst->refresh();
			lst->schedule_redraw(false);
		}
	}

	endwin();
	fprintf(stdlog,"Screen was %i by %i with TOP=%p\n",SCREEN_HEIGHT,SCREEN_WIDTH,(void*)TOP_WINDOW);
	fclose(stdlog);
	return 0;
}
